#pragma once
#include <memory>
#include <vector>
#include <string>
#include <Common/headers.h>
#include <Graphics/color.h>


namespace occ {
	class TriMesh {
	public:
		TriMesh(const Color& _color = DefaultColor::Yellow()) :color(_color) {}

		std::vector<vec3f> vp, vn;
		std::vector<glm::u32vec3> face;
		std::vector<vec3f> face_center, face_normal;  // face properties
		Color color;  // texture unsupported yet, use color instead

		size_t nVertices() const {
			return vp.size();
		}

		size_t nFaces() const {
			return face.size();
		}

		bool hasVertexNormal() const { 
			return vn.size();
			//return false;
		}
		bool hasFaceCenter() const { return face_center.size(); }
		bool hasFaceNormal() const { return face_normal.size(); }

		void clearVertexNormal() { vn.clear(); }
		void clearFaceCenter() { face_center.clear(); }
		void clearFaceNormal() { face_normal.clear(); }

		// compute face properties including face center and face normal
		// a better practice is to use flags to specify which properties to compute
		void computeFaceProperties() {
			face_center.clear();
			face_center.reserve(face.size());
			face_normal.clear();
			face_normal.reserve(face.size());
			for (int i = 0; i < face.size(); ++i) {
				auto& v1 = vp[face[i].x];
				auto& v2 = vp[face[i].y];
				auto& v3 = vp[face[i].z];
				face_center.emplace_back((v1 + v2 + v3) / occFloat(3.0));
				if (hasVertexNormal()) {
					auto& vn1 = vn[face[i].x];
					auto& vn2 = vn[face[i].y];
					auto& vn3 = vn[face[i].z];
					face_normal.emplace_back(glm::normalize((vn1 + vn2 + vn3) / occFloat(3.0)));
				}
				else
					face_normal.emplace_back(glm::normalize(glm::cross(v2 - v1, v3 - v1)));
				
			}
		}

		virtual void clear() {
			vp.clear();
			vn.clear();
			face.clear();
			face_center.clear();
			face_normal.clear();
		}
	};
	/*
	class TriMesh_FaceExtended : public TriMesh {
	public:
		TriMesh_FaceExtended(const Color& _color = DefaultColor::Yellow())
			:TriMesh(_color) {}

		void clear() override {
			TriMesh::clear();
			face_normal.clear();
			face_center.clear();
		}
		
		std::vector<vec3f> face_normal;
		std::vector<vec3f> face_center;
	};*/

	class Model {
	public:
		std::vector<std::shared_ptr<TriMesh>> meshes;
		std::vector<mat4f> local2model;

		size_t nComponents() const {
			return meshes.size();
		}

		virtual void clear();

		friend std::shared_ptr<Model> loadModelFromFile(const std::string& path);
	};

	std::shared_ptr<Model> loadModelFromFile(const std::string& path);

	class Scene {
	public:
		std::vector<std::shared_ptr<Model>> models;
		std::vector<mat4f> model2world;

		size_t nComponents() const {
			return models.size();
		}

		virtual void clear() {
			models.clear();
			model2world.clear();
		}
	};
}